/*
 * WorkSnmpApi.cpp
 *
 *  Created on: 2017-5-25
 *      Author: root
 */
#include"WorkSnmpApi.h"
#if USE_PUBLIC_SNMP
#include"PublicTool.h"
#include"PublicLog.h"
#include"snmp_packet.h"
#include"PublicSocket.h"
typedef struct _IPSNMPCHECK{
char     ip[40];//IP
struct   timeval timestamp;//时间戳
//int      checktimes;//已检测次数
#if 0
bool operator < (struct _IPSNMPSTATUS const &other)const{
    if(strcmp(ip,other.ip)<0)
        return true;
    return false;
}
#endif
}IPSNMPCHECK;
typedef std::map<int,IPSNMPCHECK>MAPIPSNMPCHECK;
typedef std::map<std::string,int>MAPIPCHECKTIMES;
//初始化SNMP风格
SNMPFLAG InitSnmpFlag(){
    SNMPFLAG flag={0};
    strcpy(flag.Communtiy,"Public");
    flag.ReTimes=3;
    flag.TimeOut=1500;
    flag.PerNum=10;
    flag.PerTime=10;//发送间隔10ms
	flag.bSupportIPV6=IsSupportIPV6();
    return flag;
}
//显示SNMP风格
void ShowSnmpFlag(SNMPFLAG flag,size_t num){
    INFO("数量:%zd 重试次数:%d 超时:%d ms 发送间隔:%d ms 单次数量:%d 团体号:%s 源IP:%s",
    num,flag.ReTimes,flag.TimeOut,flag.PerTime,flag.PerNum,flag.Communtiy,flag.SrcIP);
}

int SendSnmpPacket(CPublicSocket*sock,const char*ip,const char*communtiy,int request_id){	
    char buffer[1024]={0};
    int buffersize=0;
    //INFOEX("开始构建队列IP:%s ID:%u\n",ip,request_id);
    buffersize=MakeSnmpSysPacket(buffer,communtiy,request_id,ip);
    //INFOEX("构建队列完毕IP:%s 长度:%d ID:%u\n",ip,buffersize,request_id);
	int ret=sock->Send(buffer,buffersize,ip,161);	
    return ret;
}

int RecvSnmpPacket(CPublicSocket*sock,SNMPSYSPACKET*sysinfo,struct timeval*recvtimestamp){
    char recvpacket[4092]={0};
    int  recvpacketsize=0;
	recvpacketsize=sock->Recv(recvpacket,sizeof(recvpacket),NULL,recvtimestamp);	
    if(recvpacketsize<1){
		return -1;
	}
    return ReadSnmpSysPacket(recvpacket,recvpacketsize,sysinfo);
}
#include"PublicRandom.h"
MAPSNMPSYSPACKET _GetSnmpInfoByIPList(SETIPLIST IPList,SNMPFLAG flag,MAPSTRINGLIST communitylist){
    MAPSNMPSYSPACKET result;
    MAPIPSNMPCHECK   checklist;
    PublicRandom     IPListTmp;
    MAPIPCHECKTIMES  checktimes;//存储重试次数
    int              sendflag=1;
    int              checkflag=0;
    int              request_id=1;
    int              sendnum=0;
    struct timeval   sendtimestamp={0,0};
    struct timeval   checktimestamp={0,0};
    gettimeofday(&sendtimestamp,NULL);
    checktimestamp=sendtimestamp;
    IPListTmp.InsertSetList(IPList);
    ShowSnmpFlag(flag,IPList.size());
	CPublicSocket sock;
	sock.CreateSocket(SOCKET_TYPE_UDP,flag.PerTime,flag.SrcIP);
    while(IPListTmp.empty()==false||checklist.empty()==false){
		if(sendflag==1&&IPListTmp.empty()==false){
			std::string ip=IPListTmp.PopList();
			MAPSTRINGLIST::iterator it=communitylist.find(ip);			
			if(it!=communitylist.end()){
				SendSnmpPacket(&sock,ip.c_str(),it->second.c_str(),request_id);
			}else{
				SendSnmpPacket(&sock,ip.c_str(),flag.Communtiy,request_id);
			}		
			timeval timestamp={0,0};
			gettimeofday(&timestamp,NULL);
			IPSNMPCHECK checkinfo;
			memset(&checkinfo,0,sizeof(IPSNMPCHECK));
			strcpy(checkinfo.ip,ip.c_str());
			checkinfo.timestamp=timestamp;
			checklist.insert(std::pair<int,IPSNMPCHECK>(request_id,checkinfo));
			request_id++;
			sendnum++;
			if(sendnum<flag.PerNum){
				continue;
			}else{
				sendtimestamp=timestamp;
				sendflag=0;
				sendnum=0;
            }
        }
		while(true){
			SNMPSYSPACKET sysinfo={0};
			struct timeval timestamp={0};	
			if(RecvSnmpPacket(&sock,&sysinfo,&timestamp)<0){			
				if(timestamp.tv_sec==0)gettimeofday(&timestamp,NULL);
				if(GetTimerSubCommon(timestamp,checktimestamp)>1000){
					checktimestamp=timestamp;
					checkflag=1;
					sendflag=0;
				}else
				if(GetTimerSubCommonEx(timestamp,sendtimestamp)>flag.PerTime){
					sendflag=1;
				}
				break;
			}
			MAPIPSNMPCHECK::iterator it=checklist.find(sysinfo.request_id);
			if(it!=checklist.end()){
				sysinfo.snmptime=GetTimerSubCommon(timestamp,it->second.timestamp);
				result.insert(std::pair<std::string,SNMPSYSPACKET>(it->second.ip,sysinfo));
				checktimes.erase(it->second.ip);
				checklist.erase(it);
			}
//队列中没有要检查的了			
			if(checklist.empty()){
				break;
			}
        }
		if(checkflag){
			checkflag=0;
			INFO("%ld.%06ld 进行一次超时检测 发送剩余:%zd 重发数量:%zd 待检测:%zd 已处理:%zd",
			checktimestamp.tv_sec,checktimestamp.tv_usec,
			IPListTmp.size(),checktimes.size(),checklist.size(),result.size());
			MAPIPSNMPCHECK checklistbak=checklist;
			checklist.clear();
			struct timeval timestamp=checktimestamp;
			MAPIPSNMPCHECK::iterator it=checklistbak.begin();
			while(it!=checklistbak.end()){
				int checktime=GetTimerSubCommon(timestamp,it->second.timestamp);
				if(checktime>flag.TimeOut){
					MAPIPCHECKTIMES::iterator it2=checktimes.find(it->second.ip);
					if(it2==checktimes.end()){
						checktimes.insert(std::pair<std::string,int>(it->second.ip,1));
						IPListTmp.PushList(it->second.ip);
					}else
					if(it2->second<flag.ReTimes){
						it2->second++;
						IPListTmp.PushList(it->second.ip);
					}else{
						SNMPSYSPACKET info={0};
						result.insert(std::pair<std::string,SNMPSYSPACKET>(it->second.ip,info));
						checktimes.erase(it->second.ip);
					}
				}else{
					checklist.insert(std::pair<int,IPSNMPCHECK>(it->first,it->second));
				}
				it++;
            }
        }
    }
    return result;
}

MAPSNMPSYSPACKET GetSnmpInfoByIPList(SETIPLIST IPList,SNMPFLAG flag,MAPSTRINGLIST communitylist){
	MAPSNMPSYSPACKET result;
    if(IPList.empty()){
		INFO("Snmp 数量:%zd 为空 源IP:%s",IPList.size(),flag.SrcIP);
		return result;
    }
	ReSetStartTime();
	if(flag.bSupportIPV6==1){
		result=_GetSnmpInfoByIPList(IPList,flag,communitylist);
	}else{
		SETIPLIST RealIPList;
		SETIPLIST::iterator it=IPList.begin();
		while(it!=IPList.end()){
			if(IsIPV6(it->c_str())==0){
				RealIPList.insert(it->c_str());
			}
			it++;
		}
		result=_GetSnmpInfoByIPList(RealIPList,flag,communitylist);
	}
	INFO("SNMP 数量:%zd 结果数量:%zd 源IP:%s 耗时:%d ms",IPList.size(),result.size(),flag.SrcIP,GetEndTime());
    return result;
}

SNMPSYSPACKET GetSnmpInfoByIP(const char*IP,const char*Communtiy,int TimeOutms){
	SNMPSYSPACKET sysinfo={0};
	struct timeval timestamp={0,0};
	struct timeval recvtimestamp={0,0};
	CPublicSocket sock;
	sock.CreateSocket(SOCKET_TYPE_UDP,TimeOutms,NULL);
	gettimeofday(&timestamp,NULL);
	SendSnmpPacket(&sock,IP,Communtiy,8888);
	if(RecvSnmpPacket(&sock,&sysinfo,&recvtimestamp)!=-1){
		sysinfo.snmptime=GetTimerSubCommon(recvtimestamp,timestamp);
	}
	return sysinfo;
}		
#endif
